﻿/*
 * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
 *
 * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include <stdexcept>
#include <unistd.h>
#include "PipeWrap.h"
#include "Util/Util.h"
#include "Util/uv_errno.h"
#include "Network/sockutil.h"

using namespace std;

#define checkFD(fd) \
    if (fd == -1) { \
        clearFd(); \
        throw runtime_error(StrPrinter << "Create windows pipe failed: " << get_uv_errmsg());\
    }

#define closeFD(fd) \
    if (fd != -1) { \
        close(fd);\
        fd = -1;\
    }

namespace toolkit {

PipeWrap::PipeWrap() {
    reOpen();
}

void PipeWrap::reOpen() {
    clearFd();
#if defined(_WIN32)
    const char *localip = SockUtil::support_ipv6() ? "::1" : "127.0.0.1";
    auto listenerFd = SockUtil::listen(0, localip);
    checkFD(listenerFd)
    SockUtil::setNoBlocked(listenerFd, false);
    auto localPort = SockUtil::get_local_port(listenerFd);
    _pipeFd[1] = SockUtil::connect(localip, localPort, false);
    checkFD(_pipeFd[1])
    _pipeFd[0] = static_cast<int>(accept(listenerFd, nullptr, nullptr));
    checkFD(_pipeFd[0])
    SockUtil::setNoDelay(_pipeFd[0]);
    SockUtil::setNoDelay(_pipeFd[1]);
    close(listenerFd);
#else
    if (pipe(_pipeFd) == -1) {
        throw runtime_error(StrPrinter << "Create posix pipe failed: " << get_uv_errmsg());
    }
#endif // defined(_WIN32)
    SockUtil::setNoBlocked(_pipeFd[0], true);
    SockUtil::setNoBlocked(_pipeFd[1], false);
    SockUtil::setCloExec(_pipeFd[0]);
    SockUtil::setCloExec(_pipeFd[1]);
}

void PipeWrap::clearFd() {
    closeFD(_pipeFd[0]);
    closeFD(_pipeFd[1]);
}

PipeWrap::~PipeWrap() {
    clearFd();
}

int PipeWrap::writeData(const void *buf, int n) {
    int ret;
    do {
#if defined(_WIN32)
        ret = send(_pipeFd[1], static_cast<const char*>(buf), n, 0);
#else
        ret = ::write(_pipeFd[1], buf, n);
#endif // defined(_WIN32)
    } while (-1 == ret && UV_EINTR == get_uv_error(true));
    return ret;
}

int PipeWrap::readData(void *buf, int n) {
    int ret;
    do {
#if defined(_WIN32)
        ret = recv(_pipeFd[0], static_cast<char*>(buf), n, 0);
#else
        ret = ::read(_pipeFd[0], buf, n);
#endif // defined(_WIN32)
    } while (-1 == ret && UV_EINTR == get_uv_error(true));
    return ret;
}

} // namespace toolkit
